package castledefense.game.screens;

import twl.global.libgdx.GlobalTWL;
import castledefense.data.BuildingData;
import castledefense.data.GameData;
import castledefense.data.Resources;
import castledefense.game.render.Cam;
import castledefense.game.render.GraphicsUtil;
import castledefense.game.render.RenderDebuff;
import castledefense.game.render.RenderMap;
import castledefense.game.render.RenderMissile;
import castledefense.game.render.RenderSpellEffect;
import castledefense.game.render.RenderUnitDeaths;
import castledefense.gui.GUIMainGame;
import castledefense.logic.Map;
import castledefense.logic.buildings.Building;
import castledefense.logic.effects.ActivatedAbilities;
import castledefense.logic.missile.MissileSystem;
import castledefense.logic.units.Unit;
import castledefense.logic.units.UnitGenerator;
import castledefense.sound.SoundHandler;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class MainGameScreen extends AbstractScreen {
	public static int mapSizeX = 51;
	public static int mapSizeY = 51;
	private GUIMainGame stateGUI;

	private boolean isFullScreen;

	public static MissileSystem missileSystem;
	// public static Light light;
	protected Map map;
	protected ActivatedAbilities activatedAbilities;

	private UnitGenerator enemyGenerator;

	protected RenderSpellEffect renderSpellEffect;
	private RenderMissile renderMissile;
	private RenderDebuff renderDebuff;
	private RenderUnitDeaths renderUnitDeaths;

	private Sprite cursor = new Sprite(Resources.cursorBuildableIm);

	public MainGameScreen(SpriteBatch spriteBatch, GlobalTWL twl) {
		super(spriteBatch);

		map = new Map(mapSizeX, mapSizeY, this);

		renderSpellEffect = new RenderSpellEffect();
		activatedAbilities = new ActivatedAbilities(map, renderSpellEffect);

		renderMissile = new RenderMissile();
		renderDebuff = new RenderDebuff();
		renderUnitDeaths = new RenderUnitDeaths();

		missileSystem = new MissileSystem(map, renderMissile, renderDebuff, renderUnitDeaths);

		enemyGenerator = new UnitGenerator(this);

		// Create the GUI for this state
		stateGUI = new GUIMainGame(this, twl);

	}

	// ---------DRAW------------------------------------------------------------
	public void draw() {
		GraphicsUtil.clear();
		Cam.normalProjection.set(spriteBatch.getProjectionMatrix());
		spriteBatch.setProjectionMatrix(Cam.getMatrix());

		spriteBatch.begin();

		// Draw map
		RenderMap.drawMap(map, spriteBatch);

		final float delta = Gdx.graphics.getDeltaTime();
		renderMissile
				.render(spriteBatch, delta, missileSystem.getMissileList());
		renderDebuff.render(spriteBatch, delta);
		renderSpellEffect.render(spriteBatch, Gdx.graphics.getDeltaTime());
		renderUnitDeaths.render(spriteBatch, delta);

		if (cursor.getTexture() != null) {
			cursor.draw(spriteBatch);
		}

		spriteBatch.end();

		spriteBatch.getProjectionMatrix().set(Cam.normalProjection);
		spriteBatch.begin();
		spriteBatch.setColor(Color.WHITE);
		Resources.arialBold24.draw(spriteBatch,
				"FPS: " + Gdx.graphics.getFramesPerSecond(),
				Gdx.graphics.getWidth() - 100, 40);

		spriteBatch.end();
		GraphicsUtil.startBlend();
		stateGUI.render();
		GraphicsUtil.endBlend();

	}

	private boolean hasBuildedSomething = false;

	public void update(int deltaTime) {
		if (deltaTime > 100)
			System.out.println("BIG PROBLEM : DeltaTime > 200");

		SoundHandler.update(deltaTime);

		activatedAbilities.update(deltaTime);
		enemyGenerator.update(deltaTime);
		missileSystem.update(deltaTime);
		renderMissile.update(deltaTime);
		renderDebuff.update(deltaTime);
		renderSpellEffect.update(deltaTime);
		renderUnitDeaths.update(deltaTime);

		map.update(deltaTime);
		// Build a building
		if (!Gdx.input.isButtonPressed(Input.Buttons.LEFT)) {
			hasBuildedSomething = false;
		}
		if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) {
			int mouseX = Gdx.input.getX();
			int mouseY = Gdx.input.getY();
			if (!stateGUI.isInsideGui(mouseX, mouseY)) {
				if (BuildingData.building == BuildingData.noBuilding) {
					Unit tmpUnit = map.selectUnit(getPosX(mouseX)/Map.cellWidth,getPosY(mouseY)/Map.cellWidth);
					if (tmpUnit != null) {
						stateGUI.getUpgradePanel().setWhichInfo(tmpUnit);
					}
				}
			}

			if (!stateGUI.isInsideGui(mouseX, mouseY)) {
				// scale everything to zoom units and then translate and use
				// that to calculate map array index
				final int posX = getGridPosX(mouseX);
				final int posY = getGridPosY(mouseY);

				if (BuildingData.money >= BuildingData
						.getCost(BuildingData.building)) {
					// All the other building
					int posY2 = posY
							- BuildingData.getSize(BuildingData.building) + 1;
					Building building = BuildingData.getBuilding(map, posX,
							posY2, BuildingData.building);
					if (building != null && !hasBuildedSomething) {
						if (map.isBuildable(building.getCellX(),
								building.getCellY(), building.getSize())) {
							map.addBasement(building);
							hasBuildedSomething = true;
						}
					}
				}
				if (BuildingData.building == BuildingData.destroy) {
					// Destroy
					if (map.hasBuilding(posX, posY)) {
						map.destroyBuilding(posX, posY);
					}
				}
			}
		}

		// Update the map position
		if (Gdx.input.isKeyPressed(Input.Keys.LEFT)
				|| Gdx.input.isKeyPressed(Input.Keys.A)) {

			Cam.moveLeft(deltaTime);
		}
		if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)
				|| Gdx.input.isKeyPressed(Input.Keys.D)) {
			Cam.moveRight(deltaTime);
		}

		if (Gdx.input.isKeyPressed(Input.Keys.UP)
				|| Gdx.input.isKeyPressed(Input.Keys.W)) {

			Cam.moveUp(deltaTime);
		}
		if (Gdx.input.isKeyPressed(Input.Keys.DOWN)
				|| Gdx.input.isKeyPressed(Input.Keys.S)) {

			Cam.moveDown(deltaTime);
		}

		cursor.setTexture(null);
		if (BuildingData.building != BuildingData.noBuilding) {
			int mouseX = Gdx.input.getX();
			int mouseY = Gdx.input.getY();
			if (!stateGUI.isInsideGui(mouseX, mouseY)) {
				int posX = getGridPosX(mouseX);
				int posY = getGridPosY(mouseY);
				int size = BuildingData.getSize(BuildingData.building);
				if (BuildingData.building == BuildingData.destroy) {
					if (map.hasBuilding(posX, posY)) {
						size = map.getSizeBuilding(posX, posY);
						int x = map.getPosXBuilding(posX, posY);
						int y = map.getPosYBuilding(posX, posY);
						cursor.setTexture(Resources.cursorSellIm);
						cursor.setSize(size * Map.cellWidth, size
								* Map.cellWidth);
						cursor.setPosition(x * Map.cellWidth, y * Map.cellWidth);
					} else {
						cursor.setTexture(Resources.cursorSellIm);
						cursor.setSize(Map.cellWidth, Map.cellWidth);
						cursor.setPosition(posX * Map.cellWidth, posY
								* Map.cellWidth);
					}
				} else {
					posY = posY - BuildingData.getSize(BuildingData.building)
							+ 1;
					if (map.isBuildable(posX, posY, size)) {
						cursor.setTexture(Resources.cursorBuildableIm);
						cursor.setSize(Map.cellWidth * size, Map.cellWidth
								* size);
						cursor.setPosition(posX * Map.cellWidth, posY
								* Map.cellWidth);
					} else {
						cursor.setTexture(Resources.cursorUnbuildableIm);
						cursor.setSize(Map.cellWidth * size, Map.cellWidth
								* size);
						cursor.setPosition(posX * Map.cellWidth, posY
								* Map.cellWidth);
					}
				}
			}
		}
	}

	public GUIMainGame getStateGUI() {
		return stateGUI;
	}

	public final ActivatedAbilities getActivatedAbilities() {
		return activatedAbilities;
	}

	public void setMapSize(int sizeX, int sizeY) {
		mapSizeX = sizeX;
		mapSizeY = sizeY;
	}

	public int getMapSizeX() {
		return mapSizeX;
	}

	public int getMapSizeY() {
		return mapSizeY;
	}

	public Map getMap() {
		return map;
	}

	public int getLevel() {
		return enemyGenerator.getLevel();
	}

	public int getLevelTime() {
		return enemyGenerator.getLevelTime();
	}

	/**
	 * @param mouseX
	 * @return The position x on the scale and translate map of the mouse
	 */
	public int getGridPosX(int mouseX) {
		return (int) ((Cam.camera.position.x + ((float) mouseX - (float) GameData.HALFSCREENSIZEX)
				* Cam.camera.zoom) / Map.cellWidth);
	}

	/**
	 * @param mouseY
	 * @return The position y on the scale and translate map of the mouse
	 */
	public int getGridPosY(int mouseY) {
		return (int) ((Cam.camera.position.y - ((float) mouseY - (float) GameData.HALFSCREENSIZEY)
				* Cam.camera.zoom) / Map.cellWidth);
	}

	public float getPosX(int mouseX) {
		return Cam.camera.position.x
				+ ((float) mouseX - (float) GameData.HALFSCREENSIZEX)
				* Cam.camera.zoom;
	}

	/**
	 * @param mouseY
	 * @return The position y on the scale and translate map of the mouse
	 */
	public float getPosY(int mouseY) {
		return Cam.camera.position.y
				- ((float) mouseY - (float) GameData.HALFSCREENSIZEY)
				* Cam.camera.zoom;
	}

	// -------------------- Input event handling methods --------------------

	@Override
	public boolean keyDown(int key) {
		if (key == Input.Keys.F10) {
			isFullScreen = !isFullScreen; 				
			Gdx.graphics.setDisplayMode(GameData.SCREENSIZEX,
						GameData.SCREENSIZEY, isFullScreen);
				
		}

		if (key == Input.Keys.ESCAPE) {
			isFullScreen = false;
			Gdx.graphics.setDisplayMode(GameData.SCREENSIZEX,
					GameData.SCREENSIZEY, isFullScreen);			
		}

		if (key == Input.Keys.M) {
			SoundHandler.setMute(!SoundHandler.isMute());
		}

		return false;
	}

	// Must be update in touchMoved and touchDragged
	private int oldX = 0;
	private int oldY = 0;

	@Override
	public boolean touchMoved(int x, int y) {
		oldX = x;
		oldY = y;
		return false;
	}

	@Override
	public boolean touchDragged(int x, int y, int pointer) {
		// map dragging with right or middle mouse
		if (Gdx.input.isButtonPressed(Input.Buttons.RIGHT)
				|| Gdx.input.isButtonPressed(Input.Buttons.MIDDLE)) {
			Cam.move(oldX - x, oldY - y);
		}
		oldX = x;
		oldY = y;
		return false;
	}

	@Override
	public boolean touchDown(int x, int y, int pointer, int button) {
		// Unselect a building
		if (button == Input.Buttons.RIGHT) {
			map.unSelectBuildings();
			map.unSelectUnit();
			stateGUI.getUpgradePanel().nullWhichInfo();
		}

		// Select a building
		if (!stateGUI.isInsideGui(x, y)) {
			if (button == Input.Buttons.LEFT
					&& (BuildingData.building == BuildingData.noBuilding)) {
				int mouseX = x;
				int mouseY = y;
				int posX = getGridPosX(mouseX);
				int posY = getGridPosY(mouseY);
				Building building = map.selectBuilding(posX, posY);
				if (building != null) {
					stateGUI.getUpgradePanel().setWhichInfo(building);
				} else {
					stateGUI.getUpgradePanel().nullWhichInfo();
				}
			}
		}
		return false;
	}

	@Override
	public boolean scrolled(int change) {
		Cam.zoom(change * 0.1f, Gdx.input.getX(), Gdx.input.getY());
		return false;
	}
}
